require 'textblock_handler' # for parsing pdftextextraction blocks output
require 'tempfile'
require 'rtimbl' # for ml classification
include Ruby_timbl

class Article < ActiveRecord::Base
  include AASM
  include TextBlockHandler
  
  has_many :tblocks, :dependent => :destroy
  has_many :pages, :dependent => :destroy, :include => :tblocks
  has_many :words, :dependent => :destroy
  
  before_destroy :delete_attachments
  after_create :set_name
  before_save :update_blocks_xml
  attr_protected :state

  # paperclip examples:
  #   http://github.com/thoughtbot/paperclip/tree/master
  has_attached_file :pdf
  has_attached_file :xml
  has_attached_file :features

  # state machine definition
  aasm_column :state
  aasm_initial_state :uploaded
  aasm_state :uploaded
  aasm_state :blockified
  aasm_state :raw
  aasm_state :classified
  aasm_state :annotated
  aasm_event :blockify do
    transitions :to => :blockified, :from => [:uploaded]
  end
  aasm_event :imagify do
    transitions :to => :raw, :from => [:blockified]
  end
  aasm_event :classify do
    transitions :to => :classified, :from => [:raw, :annotated, :classified], :on_transition => :classify_pdf
  end
  aasm_event :annotate do
    transitions :to => :annotated, :from => [:raw, :classified, :annotated]
  end

  def self.default_fxml_params
    { :include => { :pages => Page.default_fxml_params}, :methods => [:attachment_url] }
  end

  def self.show_fxml_params
    { :include => { :pages => Page.show_fxml_params}, :methods => [:attachment_url] }
  end

  def self.index_fxml_params
    {:methods => [:attachment_url] }
  end

  # attachment url to send to flex client through to_fxml serialization
  def attachment_url
    pdf.url(:original)
  end

  def delete_attachments
    self.xml.destroy
    self.pdf.destroy
    self.features.destroy
  end

  def self.train
    instances = []
    articles = Article.find_all_by_training(true)

    articles.each do |a|
      File.open(a.features.path, 'r') do |f|
        lines = f.readlines
        split_lines = lines.map { |l| l.split(/\t/) }        
        lines_hash = split_lines.inject({}) do |hash, line| 
          hash[line[0].to_s] = line[1]
          hash
        end
        tbids = split_lines.map { |l| l[0] }
        tbs = Tblock.find_all_by_article_id(a.id, 
                                            :conditions => 
                                            ["tbid in (?)", tbids])
        tbs.each do |tb|
          if tb.classification
            instances << lines_hash[tb.tbid.to_s].strip + tb.classification
          elsif tb.ml_class
            instances << lines_hash[tb.tbid.to_s].strip + tb.ml_class
          end
        end
      end
    end
    f = File.open("#{RAILS_ROOT}/lib/training.arff","w+")
    f.puts "@data"
    f.puts instances
    f.close
  end

  # blockify and imagify pdfs after they are uploaded
  def process_new_pdf
    logger.debug "blockify"
    blockify_pdf
    blockify! # transition state to 'blockified'

    logger.debug "imagify"
    imagify_pdf
    imagify! # transition state to 'raw'

    # parse blockify output and load into db.
    # we do this after imagify so we can link each tblock to its page.
    self.tbh_new_parser
    self.tbh_parse(xml.path)

  end

  private 

  # run classifier over the text blocks for the current article
  def classify_pdf
    # setup timbl model
    rt = TimblAPI.new('-k 3 -F ARFF', 'latisi_timbl')
    rt.Learn("#{RAILS_ROOT}/lib/training.arff")
    flines = File.open(features.path, 'r').readlines
    # run classifier on each tblock after getting it's feature vector
    self.tblocks.each do |tb| 
      feat_string = flines.grep(/^#{tb.tbid}\t(.*)$/)[0].split(/\t/)[1]
      feat_string += "?"
      result = rt.Classify(feat_string)
      tb.ml_class = result.Name #Tblock.class_to_long(result.name)
      tb.save
    end
  end

  # add class information to -blocks.xml file
  def update_blocks_xml(attr_hash = {})
    latest_tb = self.tblocks.find(:all, :order => "updated_at DESC", 
                                  :limit => 1)[0]
    # only update if we have a tb that is newer than the article
    if latest_tb and (latest_tb.updated_at > self.updated_at)
      xml_filename = self.xml.original_filename
      return if not File.exist?(xml_filename)
      xml_doc = Nokogiri::XML(open(self.xml.path))
      self.tblocks.each do |tb|
        if tb.updated_at > self.updated_at
          if tb.classification or tb.ml_class
            block_node = xml_doc.root.xpath("//Block[@id = '#{tb.tbid}']").first
            block_node["class"] = (tb.classification || tb.ml_class)
          end
        end
      end
      tfile = Tempfile.new(self.xml.path)
      tfile.puts xml_doc.to_xml
      # update model attachment with new xml file
      blocks_filename = self.xml_file_name
      self.xml = tfile
      self.xml.instance_write(:file_name, xml_filename)
      #self.save
    end
  end

  def set_name
    logger.debug "pdf_file_name: #{pdf_file_name}"
    if pdf_file_name
      self.name = pdf_file_name.sub(/.pdf\Z/, "")
    end
    self.save
  end

  def blockify_pdf
    Dir.mktmpdir do |xml_dir|
      # call blockify.jar with xml_dir as output directory
      pdf_dir = File.dirname(pdf.path)
      Dir.chdir(xml_dir) do
        # need this directory so java program doesn't complain
        Dir.mkdir("test-results")
        input_suffix = ".pdf"
        output_suffix = "-blocks.xml"
        #blockify_cmd = "java -Xmx1024m -jar #{RAILS_ROOT}/lib/dist/pdfextraction.jar"
        #blockify_cmd += " blockify --input #{pdf_dir} " +
        #"--output #{xml_dir} --threads 1 --jpedal true"
        classpath = "$CLASSPATH:#{RAILS_ROOT}/lib/dist/lib/antlr-2.7.5.jar:#{RAILS_ROOT}/lib/dist/lib/commons-jci-SNAPSHOT20051110.jar:#{RAILS_ROOT}/lib/dist/lib/commons-logging-1.0.4.jar:#{RAILS_ROOT}/lib/dist/lib/drools-base-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-core-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-decisiontables-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-io-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-java-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-smf-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/EasyTimbl.jar:#{RAILS_ROOT}/lib/dist/lib/jai_codec.jar:#{RAILS_ROOT}/lib/dist/lib/jai_core.jar:#{RAILS_ROOT}/lib/dist/lib/jdtcore-3.1.0.jar:#{RAILS_ROOT}/lib/dist/lib/JpedalBasics.jar:#{RAILS_ROOT}/lib/dist/lib/jsi-1.0b2.jar:#{RAILS_ROOT}/lib/dist/lib/junit-3.8.1.jar:#{RAILS_ROOT}/lib/dist/lib/junit.jar:#{RAILS_ROOT}/lib/dist/lib/log4j-1.2.6.jar:#{RAILS_ROOT}/lib/dist/lib/Multivalent20040415.jar:#{RAILS_ROOT}/lib/dist/lib/trove-0.1.8.jar:#{RAILS_ROOT}/lib/dist/lib/uima-adapter-soap.jar:#{RAILS_ROOT}/lib/dist/lib/uima-adapter-vinci.jar:#{RAILS_ROOT}/lib/dist/lib/uima-core.jar:#{RAILS_ROOT}/lib/dist/lib/uima-cpe.jar:#{RAILS_ROOT}/lib/dist/lib/uima-document-annotation.jar:#{RAILS_ROOT}/lib/dist/lib/uima-examples.jar:#{RAILS_ROOT}/lib/dist/lib/uima-tools.jar:#{RAILS_ROOT}/lib/dist/PDFExtraction.jar"
        blockify_cmd = "java -Xmx1024m -classpath #{classpath} edu.isi.bmkeg.pdfextraction.PDFExtractionAPI " + 
          "blockify --input #{pdf_dir} --output #{xml_dir} --threads 1 --jpedal true"
        logger.debug "blockify command: #{blockify_cmd}"
        puts "blockify command: #{blockify_cmd}"
        system(blockify_cmd)
      end

      # store xml file as attachment
      xml_filename = File.basename(pdf.path).gsub(".pdf", "-blocks.xml")
      logger.debug "xml_filename is: #{xml_dir}/#{xml_filename}"
      self.xml = File.open(xml_dir + "/" + xml_filename, "r")
      # store features file as attachment
      feat_filename = File.basename(pdf.path).gsub(".pdf", ".features")
      self.features = File.open(xml_dir + "/" + feat_filename, "r")
      self.save # save article so xml and feature attachments will be saved


    end
  end

  def imagify_pdf
    pdf_dir = File.dirname(pdf.path)
    xml_dir = File.dirname(xml.path)
    Dir.mktmpdir do |image_dir|
      Dir.chdir(image_dir) do
        Dir.mkdir("test-results")
        #imagify_cmd = "java -Xmx1024m -jar #{RAILS_ROOT}/lib/dist/pdfextraction.jar"
        #imagify_cmd += " imagify --pdfs #{pdf_dir} --images #{image_dir}"
classpath = "$CLASSPATH:#{RAILS_ROOT}/lib/dist/lib/antlr-2.7.5.jar:#{RAILS_ROOT}/lib/dist/lib/commons-jci-SNAPSHOT20051110.jar:#{RAILS_ROOT}/lib/dist/lib/commons-logging-1.0.4.jar:#{RAILS_ROOT}/lib/dist/lib/drools-base-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-core-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-decisiontables-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-io-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-java-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/drools-smf-2.5-final.jar:#{RAILS_ROOT}/lib/dist/lib/EasyTimbl.jar:#{RAILS_ROOT}/lib/dist/lib/jai_codec.jar:#{RAILS_ROOT}/lib/dist/lib/jai_core.jar:#{RAILS_ROOT}/lib/dist/lib/jdtcore-3.1.0.jar:#{RAILS_ROOT}/lib/dist/lib/JpedalBasics.jar:#{RAILS_ROOT}/lib/dist/lib/jsi-1.0b2.jar:#{RAILS_ROOT}/lib/dist/lib/junit-3.8.1.jar:#{RAILS_ROOT}/lib/dist/lib/junit.jar:#{RAILS_ROOT}/lib/dist/lib/log4j-1.2.6.jar:#{RAILS_ROOT}/lib/dist/lib/Multivalent20040415.jar:#{RAILS_ROOT}/lib/dist/lib/trove-0.1.8.jar:#{RAILS_ROOT}/lib/dist/lib/uima-adapter-soap.jar:#{RAILS_ROOT}/lib/dist/lib/uima-adapter-vinci.jar:#{RAILS_ROOT}/lib/dist/lib/uima-core.jar:#{RAILS_ROOT}/lib/dist/lib/uima-cpe.jar:#{RAILS_ROOT}/lib/dist/lib/uima-document-annotation.jar:#{RAILS_ROOT}/lib/dist/lib/uima-examples.jar:#{RAILS_ROOT}/lib/dist/lib/uima-tools.jar:#{RAILS_ROOT}/lib/dist/PDFExtraction.jar"
        imagify_cmd = "java -Xmx1024m -classpath #{classpath} edu.isi.bmkeg.pdfextraction.PDFExtractionAPI " + 
          "imagify --pdfs #{pdf_dir} --images #{image_dir}"
        logger.debug "imagify command: #{imagify_cmd}"
        puts "imagify command: #{imagify_cmd}"

        system(imagify_cmd)
        Dir.glob(image_dir + "/*.png") do |image_file|
          number = image_file.match(/_(\d*).png\Z/)[1].to_i
          self.pages.build(:page_image => File.open(image_file), :number => number)
        end
      end
    end
    self.save # save article so new pages records will be saved
  end

end
